#ifndef _XX_YY_BMOCK_IN_OUT_RAW_MEM
#define _XX_YY_BMOCK_IN_OUT_RAW_MEM
#include <boost/shared_array.hpp>
#include <boost/lambda/bind.hpp>
#include <algorithm>
#include <cstring>
#include <set>
#include "detail/bmock_raw_len.hpp"

namespace bmock {
	template<typename T>
	struct in_out_T_len {
		in_out_T_len()
			:raw_len_()
		{}
		in_out_T_len(const size_t i_l,const size_t o_l)
			:raw_len_(sizeof(T),i_l,o_l)
		{}
		in_out_T_len<T> &operator = (const in_out_T_len<T> &tl) {
			raw_len_ = tl.raw_len_;
			return *this;
		}
		size_t get_full_length() const {
			return raw_len_.get_T_length();
		}
		size_t get_pad_length() const {
			return raw_len_.get_pad_length()/sizeof(T);
		}
		T *copy_to(T *ptr) const {
			return reinterpret_cast<T *>(
				raw_len_.put(reinterpret_cast<unsigned char *>(ptr))
			);
		}
	private:
		raw_len raw_len_;
	};
	template<typename T>
	struct in_out_raw_mem_stream {
		in_out_raw_mem_stream(T *const p)
			:ptr_(p)
		{}
		in_out_raw_mem_stream<T> &operator <<(const std::pair<const T*const,const T*const> &p) {			
			memcpy(ptr_, p.first, (const unsigned char* const)p.second - (const unsigned char* const)p.first);
			ptr_ += (p.second - p.first);
			return *this;
		}
		in_out_raw_mem_stream<T> &operator <<(const in_out_T_len<T> &l) {
			ptr_ = l.copy_to(ptr_);
			return *this;
		}
	private:
		T *ptr_;
	};
	struct in_out_raw_mem_descr_base {
		virtual ~in_out_raw_mem_descr_base() {}		
		static bool exist(const void* ptr);
	protected:
		void insert_ptr(const void* ptr);
		void remove_ptr(const void* ptr);
		static std::set<const void*> current_ptrs_;
	};
	typedef boost::shared_ptr<in_out_raw_mem_descr_base> in_out_raw_mem_descr_ptr;
	template<typename T>
	struct in_out_raw_mem_descr : public in_out_raw_mem_descr_base {
		in_out_raw_mem_descr(const T *const i,const size_t i_l,const T *const o,const size_t o_l) 
			:pad_(i_l,o_l)
			,ptr_(new T[pad_.get_full_length()])
		{
			copy_data(i,i_l,o,o_l);
			insert_ptr((void*)ptr_.get());
		}
		operator T *() const {
			return (T *)get_ptr();
		}
		operator T &() const {
			return *(T *)get_ptr();
		}
		const T *get_ptr() const {
			return ptr_.get();
		}
		~in_out_raw_mem_descr() {
			remove_ptr(ptr_.get());
		}
	private:		
		void copy_data(const T *const i,const size_t i_l,const T *const o,const size_t o_l) {
			in_out_raw_mem_stream<T>(ptr_.get()) << pad_
				<< std::make_pair(i,i+i_l)
				<< std::make_pair(o,o+o_l);
		}

		in_out_T_len<T>        pad_;
		boost::shared_array<T> ptr_;
	};
	template<typename T>
	inline in_out_raw_mem_descr<T> in_out_raw_mem() {
		return in_out_raw_mem_descr<T>(NULL,0,NULL,0);
	}
	template<typename T>
	inline in_out_raw_mem_descr<T> in_out_raw_mem(const T *const i,const size_t i_l,const T *const o,const size_t o_l) {
		return in_out_raw_mem_descr<T>(i,i_l,o,o_l);
	}
	template<typename T>
	inline in_out_raw_mem_descr<T> in_out_raw_mem(const T *const i,const T *const o) {
		return in_out_raw_mem(i,1,o,1);
	}
	template<typename T>
	inline in_out_raw_mem_descr<T> in_out_raw_mem(T *const i,T *const o) {
		return in_out_raw_mem(i,1,o,1);
	}
	template<typename T>
	inline in_out_raw_mem_descr<T> in_out_raw_mem(const T& i,const T &o) {
		return in_out_raw_mem(&i,1,&o,1);
	}
	inline in_out_raw_mem_descr<char> in_out_str() {
		return in_out_raw_mem<char>(NULL,0,NULL,0);
	}
	inline in_out_raw_mem_descr<char> in_out_str(const char *const str1,const char *const str2) {
		return in_out_raw_mem<char>(str1,strlen(str1)+1,str2,strlen(str2)+1);
	}
	inline in_out_raw_mem_descr<char> in_out_str(const unsigned char *const str1,const unsigned char *const str2) {
		return in_out_str((const char *const)str1,(const char *const)str2);
	}
	template<typename T>
	inline in_out_raw_mem_descr<T> out_raw_mem(const T *const o,const size_t o_l=1) {
		return in_out_raw_mem((const T*const)0,size_t(0),o,o_l);
	}
	template<typename T>
	inline in_out_raw_mem_descr<T> out_raw_mem(const T &o) {
		return out_raw_mem(&o,1);
	}
	template<typename T>
	inline in_out_raw_mem_descr<T> in_raw_mem(const T *const i,const size_t i_l=1) {
		return in_out_raw_mem(i,i_l,(const T*const)0,size_t(0));
	}
	template<typename T>
	inline in_out_raw_mem_descr<T> in_raw_mem(const T &i) {
		return in_raw_mem(&i,1);
	}
	inline in_out_raw_mem_descr<char> out_str(const char *const str2) {
		return in_out_raw_mem<char>(NULL,0,str2,strlen(str2)+1);
	}
	inline in_out_raw_mem_descr<char> out_str(const unsigned char *const str2) {
		return out_str((const char *const)str2);
	}
	inline in_out_raw_mem_descr<char> in_str(const char *const str1) {
		return in_out_raw_mem<char>(str1,strlen(str1)+1,NULL,0);
	}
	inline in_out_raw_mem_descr<char> in_str(const unsigned char *const str1) {
		return in_str((const char *const)str1);
	}
};

#endif